/* 
 * HaoRan ImageFilter Classes v0.1
 * Copyright (C) 2012 Zhenjun Dai
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation.
 */

package HaoRan.ImageFilter;

import java.lang.reflect.Array;

/**
 * 高斯模糊filter
 * @author daizhj
 *
 */
public class GaussianBlurFilter  implements IImageFilter{
	protected static int Padding = 3;

    /// <summary>
    /// The bluriness factor. 
    /// Should be in the range [0, 40].
    /// </summary>
    public float Sigma = 0.75f;
    
    float[] ApplyBlur(float[] srcPixels, int width, int height)
    {
       float[] destPixels = new float[srcPixels.length];
       System.arraycopy(srcPixels, 0, destPixels, 0, srcPixels.length);

       int w = width + Padding*2;
       int h = height + Padding*2;

       // Calculate the coefficients
       float q = Sigma;
       float q2 = q * q;
       float q3 = q2 * q;

       float b0 = 1.57825f + 2.44413f * q + 1.4281f * q2 + 0.422205f * q3;
       float b1 = 2.44413f * q + 2.85619f * q2 + 1.26661f * q3;
       float b2 = -(1.4281f * q2 + 1.26661f * q3);
       float b3 = 0.422205f * q3;

       float b = 1.0f - ((b1 + b2 + b3) / b0);

       // Apply horizontal pass
       ApplyPass(destPixels, w, h, b0, b1, b2, b3, b);

       // Transpose the array
       float[] transposedPixels = new float[destPixels.length];
       Transpose(destPixels, transposedPixels, w, h);

       // Apply vertical pass
       ApplyPass(transposedPixels, h, w, b0, b1, b2, b3, b);

       // transpose back
       Transpose(transposedPixels, destPixels, h, w);

       return destPixels;
    }

    void ApplyPass(float[] pixels, int width, int height, float b0, float b1, float b2, float b3, float b)
    {
    	float num = 1f / b0;
	    int triplewidth = width * 3;
	    for (int i = 0; i < height; i++) {
	        int steplength = i * triplewidth;
	        for (int j = steplength + 9; j < (steplength + triplewidth); j += 3) {
	            pixels[j] = (b * pixels[j]) + ((((b1 * pixels[j - 3]) + (b2 * pixels[j - 6])) + (b3 * pixels[j - 9])) * num);
	            pixels[j + 1] = (b * pixels[j + 1]) + ((((b1 * pixels[(j + 1) - 3]) + (b2 * pixels[(j + 1) - 6])) + (b3 * pixels[(j + 1) - 9])) * num);
	            pixels[j + 2] = (b * pixels[j + 2]) + ((((b1 * pixels[(j + 2) - 3]) + (b2 * pixels[(j + 2) - 6])) + (b3 * pixels[(j + 2) - 9])) * num);
	        }
	        for (int k = ((steplength + triplewidth) - 9) - 3; k >= steplength; k -= 3){
	            pixels[k] = (b * pixels[k]) + ((((b1 * pixels[k + 3]) + (b2 * pixels[k + 6])) + (b3 * pixels[k + 9])) * num);
	            pixels[k + 1] = (b * pixels[k + 1]) + ((((b1 * pixels[(k + 1) + 3]) + (b2 * pixels[(k + 1) + 6])) + (b3 * pixels[(k + 1) + 9])) * num);
	            pixels[k + 2] = (b * pixels[k + 2]) + ((((b1 * pixels[(k + 2) + 3]) + (b2 * pixels[(k + 2) + 6])) + (b3 * pixels[(k + 2) + 9])) * num);
	        }
	    }
    }
  

    void Transpose(float[] input, float[] output, int width, int height)
    {
    	for (int i = 0; i < height; i++){
            for (int j = 0; j < width; j++){
                int index = (j * height) * 3 + (i * 3);
                int pos = (i * width) * 3 + (j * 3);
                output[index] = input[pos];
                output[index + 1] = input[pos + 1];
                output[index + 2] = input[pos + 2];
            }
        }
    }
    

    float[] ConvertImageWithPadding(Image imageIn, int width, int height)
    {
    	int newheight = height + Padding*2;
        int newwidth = width + Padding*2;
        float[] numArray = new float[(newheight * newwidth) * 3];
        int index = 0;
        int num = 0;
        for (int i = -3; num < newheight; i++)
        {
            int y = i;
            if (i < 0){
                y = 0;
            }
            else if (i >= height){
                y = height - 1;
            }
            int count = 0;
            int negpadding = -1 * Padding;
            while (count < newwidth){
                int x = negpadding;
                if (negpadding < 0){
                    x = 0;
                }
                else if (negpadding >= width){
                    x = width - 1;
                }
                numArray[index] = imageIn.getRComponent(x, y) * 0.003921569f;
                numArray[index + 1] =imageIn.getGComponent(x, y) * 0.003921569f;
                numArray[index + 2] = imageIn.getBComponent(x, y) * 0.003921569f;
                
                count++; negpadding++;
                index += 3;
            }
            num++;
        }
        return numArray;
    }
    
    //@Override
    public Image process(Image imageIn) {
	    int width = imageIn.getWidth();
	    int height = imageIn.getHeight();
	    float[] imageArray = ConvertImageWithPadding(imageIn, width, height);
	    imageArray = ApplyBlur(imageArray, width, height);
	    int newwidth = width + Padding*2;
	    for (int i = 0; i < height; i++)
	    {
	        int num = ((i + 3) * newwidth) + 3;
	        for (int j = 0; j < width; j++){
	            int pos = (num + j) * 3;
	            imageIn.setPixelColor(j, i,  (byte) (imageArray[pos] * 255f),  (byte) (imageArray[pos + 1] * 255f), (byte) (imageArray[pos + 2] * 255f));
	        }
	    }
	    return imageIn;
    }

}
